package com.fsh.anime.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fsh.anime.common.RedisConstant;
import com.fsh.anime.mapper.*;
import com.fsh.anime.model.dtos.EmailLoginDto;
import com.fsh.anime.model.dtos.EmailRegistryDto;
import com.fsh.anime.model.dtos.PageUserDto;
import com.fsh.anime.model.dtos.UserMarkingDto;
import com.fsh.anime.model.pojos.*;
import com.fsh.anime.model.vos.PageResult;
import com.fsh.anime.model.vos.Result;
import com.fsh.anime.model.vos.UserScoreVo;
import com.fsh.anime.service.UserService;
import com.fsh.anime.utils.BaseContext;
import com.fsh.anime.utils.JwtUtil;
import io.netty.util.concurrent.DefaultThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private FanjuMapper fanjuMapper;
    @Autowired
    private UserBehaviorScoreMapper userBehaviorScoreMapper;
    @Autowired
    private UserRelationMapper userRelationMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private UserArticleMapper userArticleMapper;
    @Autowired
    private ArticleMapper articleMapper;
    @Autowired
    private ArticleContentMapper articleContentMapper;
    @Autowired
    private ArticleCoverMapper articleCoverMapper;
    @Resource
    private JavaMailSender javaMailSender;

    /**
     * 获取分页用户列表
     *
     * @param dto dto
     * @return {@link Result}
     */
    @Override
    public Result getUsers(PageUserDto dto) {
        //校验参数
        if(dto==null||dto.getCurrentPage()==null||dto.getPageSize()==null){
            return Result.error("无效参数");
        }
        //分页参数
        IPage page=new Page(dto.getCurrentPage(), dto.getPageSize());
        //条件参数
        LambdaQueryWrapper<User> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        if(dto.getStatus()!=null&&dto.getStatus()!=2){
            lambdaQueryWrapper.eq(User::getStatus,dto.getStatus());
        }//
        lambdaQueryWrapper.like(dto.getKeywords()!=null&&dto.getKeywords()!="",User::getName,dto.getKeywords());

        lambdaQueryWrapper.orderByDesc(User::getCreateTime);
       //开始分页
        IPage page1 = userMapper.selectPage(page, lambdaQueryWrapper);
        //封装数据
        PageResult pageResult=new PageResult();
        pageResult.setTotal(page1.getTotal());
        pageResult.setRecords(page1.getRecords());
        //返回
        return Result.success(pageResult);
    }

    /**
     * 添加用户
     */
    @Override
    public Result addUser(User user) {
        if(user==null||user.getName()==null||user.getEmail()==null||user.getOpenId()==null){
            return Result.error("请填写完整");
        }
        //新增的逻辑
        if(user.getId()==null){
            //默认密码是123abc，MD5加密
            String md5DigestAsHex = DigestUtils.md5DigestAsHex("123abc".getBytes());
            user.setPassword(md5DigestAsHex);
            //id数据库自增，status数据库默认为1，createTime自动填充
            userMapper.insert(user);
            return Result.success("添加成功");
        }
        //更新的逻辑
        userMapper.updateById(user);
        return Result.success("更新成功");
    }

    /**
     * 更新用户状态
     *
     * @param id     id
     * @param status 状态
     * @return {@link Result}
     */
    @Override
    public Result updateUserStatus(Integer id, Integer status) {
        if(id==null || status==null){
            return Result.error("无效参数");
        }
        User user=new User();
        user.setId(id);
        user.setStatus(status);
        userMapper.updateById(user);
        return Result.success("更新成功");
    }

    /**
     * 删除用户
     *
     * @param id id
     * @return {@link Result}
     */
    @Override
    public Result delUser(Integer id) {
        if(id==null){
            return Result.error("无效参数");
        }
        //status=1,正常的账号不能删除。
        User user = userMapper.selectById(id);
        if(user.getStatus()==1){
            return Result.error("'正常' 状态的用户不能删除");
        }

        userMapper.deleteById(id);
        return Result.success("删除成功");
    }

    //-----------------用户界面------------------------

    /**
     * 用户邮箱登录
     *
     * @param dto dto
     * @return {@link Result}
     */
    @Override
    public Result emailLogin(EmailLoginDto dto) {
        //校验参数 - 自动校验

        //比对 数据库 name 和 密码
        LambdaQueryWrapper<User> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getEmail,dto.getEmail());
        lambdaQueryWrapper.eq(User::getPassword, DigestUtils.md5DigestAsHex(dto.getPassword().getBytes()));
        User user = userMapper.selectOne(lambdaQueryWrapper);
        if (user==null){
            return Result.error("用户邮箱不存在或密码错误");
        }
        if(user.getStatus()==0){
            return  Result.error("账户封禁中");
        }
        //封装自定义信息
        Map<String,Object> claims=new HashMap<>();
        claims.put("id",user.getId());
        claims.put("role",user.getRole());
        //使用JWT工具类，生成身份令牌
        String token = JwtUtil.getToken(claims);
        return Result.success(token,"登陆成功");
    }


    /**
     * 得到当前用户的评分和该番剧的总分
     *
     * @param id id 番剧的id
     * @return {@link Result}
     */
    @Override
    public Result getScore(Integer id) {
        log.info("getScore线程，{}",Thread.currentThread().getId());
        //查询 该番剧的 分数
        Fanju fanju = fanjuMapper.selectById(id);
        //查询当前用户的评分
        LambdaQueryWrapper<UserBehaviorScore> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserBehaviorScore::getUserId,BaseContext.getCurrentId()).eq(UserBehaviorScore::getFanjuId,id);
        UserBehaviorScore userBehaviorScore = userBehaviorScoreMapper.selectOne(lambdaQueryWrapper);
        //封装数据
        UserScoreVo userScoreVo=new UserScoreVo();
        if(userBehaviorScore==null){
            userScoreVo.setScore(0);
        }else{
            userScoreVo.setScore(userBehaviorScore.getScore());
        }
        userScoreVo.setLike(fanju.getLike());
        return Result.success(userScoreVo);
    }

    /**
     * 当前登录用户 打分
     *
     * @param dto dto
     * @return {@link Result}
     */
    @Override
    @Transactional
    public Result userMarking(UserMarkingDto dto) {
        log.info("打分线程，{}",Thread.currentThread().getId());


        //根据当前登录的userId 和 fanjuId 确定唯一记录
        LambdaQueryWrapper<UserBehaviorScore> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserBehaviorScore::getUserId,BaseContext.getCurrentId()).eq(UserBehaviorScore::getFanjuId,dto.getFanjuId());
        UserBehaviorScore userBehaviorScore = userBehaviorScoreMapper.selectOne(lambdaQueryWrapper);
        if(userBehaviorScore==null){
            //没有该记录，直接插入
            UserBehaviorScore score=new UserBehaviorScore(null,BaseContext.getCurrentId(),dto.getFanjuId(),dto.getScore());
            //插入打分表
            userBehaviorScoreMapper.insert(score);
            //更新番剧的分数
            Fanju fanju=new Fanju();
            fanju.setId(dto.getFanjuId());
            fanju.setLike(dto.getLike()+ dto.getScore());
            fanjuMapper.updateById(fanju);
        }else{
            //有记录，更新分数
            //更新番剧的总分
            Fanju fanju=new Fanju();
            fanju.setId(dto.getFanjuId());
            fanju.setLike(dto.getLike()-userBehaviorScore.getScore()+dto.getScore());
            fanjuMapper.updateById(fanju);
            //更新用户打分
            userBehaviorScore.setScore(dto.getScore());
            userBehaviorScoreMapper.updateById(userBehaviorScore);
        }
        return Result.success("打分成功");
    }

    /**
     * 关注作者
     *
     * @param followId  被关注作者的id
     * @param followsUp 关注行为
     * @return {@link Result}
     */
    @Override
    @Transactional
    public Result follow(Long followId, Integer followsUp) {
        if(followId==null || followsUp==null){
            return Result.error("关注失败");
        }
        //当前登录用户
        Integer userId = BaseContext.getCurrentId();
        //redis中的key
        String key= RedisConstant.USER_FOLLOW_PRE+userId;
        //不能关注自己 (可以和第一句合并)
        if(followId==userId.longValue()){
            return Result.error("不能关注自己");
        }
        //关注
        if(followsUp==1){
            //先往MySQL中添加记录，再操作redis（设置过期时间2h）
            int count = userRelationMapper.insert(new UserRelation(null, userId.longValue(), followId));
            //被关注的作者 粉丝fans +1
             count+= userArticleMapper.update(null, new UpdateWrapper<UserArticle>().setSql("fans=fans+1").eq("user_id", followId));
            //该用户的 关注数follows+1
            count+=userArticleMapper.update(null,new UpdateWrapper<UserArticle>().setSql("follows=follows+1").eq("user_id",userId));
            if (count==3){
                //用户关注的作者id 都会放在set集合中。设置过期时间2h
                stringRedisTemplate.opsForSet().add(key,followId.toString());
                stringRedisTemplate.expire(key,2, TimeUnit.HOURS);
            }
        }else{ //取消关注
            //先往MySQL中删除记录，再操作redis。
            LambdaQueryWrapper<UserRelation> lambdaQueryWrapper=new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(UserRelation::getUserId,userId).eq(UserRelation::getFollowId,followId);
            int count = userRelationMapper.delete(lambdaQueryWrapper);
            //被关注的作者 粉丝fans -1
            count+= userArticleMapper.update(null, new UpdateWrapper<UserArticle>().setSql("fans=fans-1").eq("user_id", followId));
            //该用户的 关注数follows-1
            count+=userArticleMapper.update(null,new UpdateWrapper<UserArticle>().setSql("follows=follows-1").eq("user_id",userId));
            if(count==3){
                //用户关注的作者id 从set集合中删除。
                stringRedisTemplate.opsForSet().remove(key,followId.toString());
            }
        }
        //同时返回该文章作者的粉丝量
        UserArticle userArticle = userArticleMapper.selectOne(new QueryWrapper<UserArticle>().select("fans").eq("user_id", followId));
        return Result.success(userArticle.getFans());
    }

    /**
     * 删除 当前用户的文章
     *      删除三张表中的记录
     */
    @Override
    @Transactional
    public Result delArticle(String id) {
        int num1 = articleMapper.deleteById(id);
//        int num2= articleContentMapper.deleteById(id);
//        int num3 = articleCoverMapper.delete(new QueryWrapper<ArticleCover>().eq("article_id",id));
//        if (num1+num2+num3==3){
//            return Result.success("删除成功");
//        }
        return Result.error("删除成功");
    }

    /**
     * //查询该用户是否已经注册过并授权过。（特征为：既有openId，也有昵称和头像）
     *
     * @param openId
     */
    @Override
    public User selectByOpenId(String openId) {
        User user = userMapper.selectOne(new QueryWrapper<User>().eq("open_id", openId));
        return user;
    }

    /**
     * 微信用户注册
     */
    @Override
    public void register(User u) {
        userMapper.insert(u);
    }

    private ThreadPoolExecutor executor=new ThreadPoolExecutor(
            5,10,60,TimeUnit.SECONDS
            ,new LinkedBlockingQueue<>(100)
            ,new DefaultThreadFactory("send_email_code-")
            ,new ThreadPoolExecutor.CallerRunsPolicy()
    );

    /**
     * 发送邮箱验证码
     *
     * @param email
     */
    @Override
    public Result sendEmailCode(String email) {
        //配置信息
        String from = "2782877649@qq.com(动漫星球)";
        String to = email;

        String code= RandomUtil.randomNumbers(6);
        String subject="验证码";
        String content="<h2>"+code+"</h2>,请不要泄露验证码，有效期为1h";
        //将验证码存到redis中
        stringRedisTemplate.opsForValue().set(email,code,1,TimeUnit.HOURS);

        MimeMessage mimeMessage=javaMailSender.createMimeMessage();
        MimeMessageHelper helper=new MimeMessageHelper(mimeMessage);
        try {
            helper.setFrom(from);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content,true);
        }catch (MessagingException e){
            e.printStackTrace();
        }
        //异步发送邮件
        executor.submit(()->{
            javaMailSender.send(mimeMessage);
        });
        return Result.success("发送成功");
    }

    /**
     * 邮箱注册
     */
    @Override
    public Result emailRegister(EmailRegistryDto dto) {
        String code = stringRedisTemplate.opsForValue().get(dto.getEmail());
        if (StringUtils.isBlank(code)){
            return Result.error("验证码已过期或未发送验证码");
        }
        if (!StringUtils.equals(code,dto.getCode().toString())){
            return Result.error("验证码错误");
        }

        //注册账号
        User user=User.builder()
                .name(dto.getUserName())
                .email(dto.getEmail())
                .password(DigestUtils.md5DigestAsHex(dto.getPwd().getBytes()))
                .role("_user").build();
        userMapper.insert(user);
        return Result.success("注册成功");
    }
}
